package com.easystudy.config;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import com.easystudy.oauth.UsernameUserDetailService;

/**
 * @文件名称: AuthorizationServerConfig.java
 * @功能描述: 授权服务配置类，配置token存储方式、clientDetail等
 * @版权信息： www.easystudy.com
 * @技术交流： 961179337(QQ群)
 * @编写作者： lixx2048@163.com
 * @个人信息：941415509
 * @开发日期： 2020年7月27日
 * @历史版本： V1.0
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	// 用户认证管理器
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;
    // JDBC存储数据源
    @Autowired
    private DataSource dataSource;
    // 用户权限接口实现
    @Autowired
    private UsernameUserDetailService userDetailsService;
    
//    @Autowired
//    private PasswordEncoder passwordEncoder;
    
    /**
     * @功能描述: 定义令牌端点上的安全性约 束
     * @技术交流： 961179337(QQ群)
     * @编写作者： lixx2048@163.com
     * @开发日期： 2020年8月3日
     * @参数说明：
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()");
        security.checkTokenAccess("isAuthenticated()");
        security.allowFormAuthenticationForClients();
    }
    
    /**
     * @功能描述: 用于定义客户端详细信息服务的配置程序。可以初始化客户端详细信息，也可以只引用现有
     * @技术交流： 961179337(QQ群)
     * @编写作者： lixx2048@163.com
     * @开发日期： 2020年8月3日
     * @参数说明：
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    	// 配置客户端：使用JDBC数据源加载用户信息
    	clients.jdbc(dataSource);
    	
    	// 配置客户端：也可以自己加载后设置到内存中
    	// 配置客户端：自定义内存用户信息
//    	clients
//    		// 内存设置
//    		.inMemory()
//    		// 设置客户端id
//    		.withClient("my_client_id")
//    		// 设置客户端秘钥
//    		.secret(passwordEncoder.encode("my_client_secret"))
//    		// token失效时间(s)
//    		.accessTokenValiditySeconds(7 * 24 * 3600)
//    		// 刷新token有效时间
//    		.accessTokenValiditySeconds(14 * 24 * 3600)
//    		// 授权类型
//    		.authorizedGrantTypes("authorization_code","refresh_token","implicit","password","client_credentials")
//    		// 自动动过-无需用户确认
//    		.autoApprove(true)
//    		// 权限设置
//    		.authorities("user:add", "user:del")
//    		// 授权范围
//    		.scopes("read,writer");
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
        		 // 使用的认证管理器-默认包含登录认证、用户名密码认证
        		 .authenticationManager(authenticationManager)
        		 // 用户账号密码加载服务接口
                 .userDetailsService(userDetailsService)
        		 // 增强策略-可以在JwtAccessTokenConverter中重载或传入
        		 //.tokenEnhancer(jwtTokenEnhancer())
        		 // 设置refresh token是否重复使用，若无，refresh_token会有UserDetailsService is required错误
        		 .reuseRefreshTokens(false)
                 // 授权请求提交方式
                 .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                 // 配置JwtAccessToken转换器【重点】
                 .accessTokenConverter(accessTokenConverter());
    }
    
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter() {
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                Map<String, Object> additionalInformation = new HashMap<>();
                additionalInformation.put("user_id", "001");
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
                return super.enhance(accessToken, authentication);
            }
        };
        // 非对称加密，但jwt长度过长
		// KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("auth2-jwt.jks"), "123456".toCharArray()).getKeyPair("auth2-jwt");
		// converter.setKeyPair(keyPair);
        // 对称加密
        converter.setSigningKey("lixx");
        return converter;
    }
    
    /**
     * @功能描述: jwt身份转换器，用于将封装在jwt里面的信息解析或者转换
     * @技术交流： 961179337(QQ群)
     * @编写作者： lixx2048@163.com
     * @开发日期： 2020年8月3日
     * @参数说明：
     */
    public JwtAccessTokenConverter jwtTokenEnhancer() {
        // RSA非对称加密密钥工厂，参数一为密钥地址，参数二为打开密钥的密码
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("auth2-jwt.jks"),"123456".toCharArray());
        JwtAccessTokenConverter converter =  new JwtAccessTokenConverter();
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("auth2-jwt","123456".toCharArray()));
        return converter;
    }
}